/**
 * 本模块定义了设备文件的各种操作事件处理回调函数，是主要的业务实现模块
 */

#include <ntddk.h>
#include <wdf.h>
#include <initguid.h>

#include "share/utils.h"
#include "trace.h"
#include "public.h"
#include "device.h"
#include "device.tmh"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, zyhelloEvtIoDeviceControl)
#pragma alloc_text(PAGE, zyhelloEvtIoRead)
#pragma alloc_text(PAGE, zyhelloEvtIoWrite)
#pragma alloc_text(PAGE, zyhelloEvtIoStop)
#endif

static size_t copyFromRequest(WDFREQUEST request, void* buf, size_t bufSize) {
    if(!buf || !bufSize) return 0;
    NTSTATUS status;

    // get input buffer
    WDFMEMORY mem;
    status = WdfRequestRetrieveInputMemory(request, &mem);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_IO, "WdfRequestRetrieveInputMemory failed: %!STATUS!", status);
        return 0;
    }

    // get memory data size in request
    size_t memSize = 0;
    if(!WdfMemoryGetBuffer(mem, &memSize) || !memSize) {
        TraceError(DBG_IO, "Error: no data in request !");
        return 0;
    }

    // copy to buffer
    size_t dataSize = MIN(memSize, bufSize);
    status          = WdfMemoryCopyToBuffer(mem, 0, buf, dataSize);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_IO, "WdfMemoryCopyToBuffer failed: %!STATUS!", status);
        return 0;
    }
    return dataSize;
}

static size_t copyToRequest(WDFREQUEST request, void* buf, size_t bufSize) {
    if(!buf || !bufSize) return 0;
    NTSTATUS status;

    // get output buffer
    WDFMEMORY mem;
    status = WdfRequestRetrieveOutputMemory(request, &mem);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_IO, "WdfRequestRetrieveInputMemory failed: %!STATUS!", status);
        return 0;
    }

    // get memory data size in request
    size_t memSize = 0;
    if(!WdfMemoryGetBuffer(mem, &memSize) || !memSize) {
        TraceError(DBG_IO, "Error: no space in request !");
        return 0;
    }

    // copy to buffer
    size_t dataSize = MIN(memSize, bufSize);
    status          = WdfMemoryCopyFromBuffer(mem, 0, buf, dataSize);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_IO, "WdfMemoryCopyFromBuffer failed: %!STATUS!", status);
        return 0;
    }
    return dataSize;
}

/**
This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.
@param Queue -  Handle to the framework queue object that is associated with the I/O request.
@param Request - Handle to a framework request object.
@param OutputBufferLength - Size of the output buffer in bytes
@param InputBufferLength - Size of the input buffer in bytes
@param IoControlCode - I/O control code.
@return VOID
*/
VOID zyhelloEvtIoDeviceControl(_In_ WDFQUEUE Queue, _In_ WDFREQUEST request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode) {
    NTSTATUS        status = STATUS_SUCCESS;
    ULONG           val;
    PDEVICE_CONTEXT deviceContext = DeviceGetContext(WdfRequestGetFileObject(request));
    //PQUEUE_CONTEXT  queueContext  = QueueGetContext(WdfRequestGetFileObject(request));
    UNREFERENCED_PARAMETER(Queue);
    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);
    TraceInfo(DBG_INIT, "%!FUNC! Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d", Queue, request, (int)OutputBufferLength, (int)InputBufferLength, IoControlCode);

    // ioctl codes defined in xdma_public.h
    switch(IoControlCode) {
    case IOCTL_ZYHELLO_SET_READ_TIMEOUT:
        TraceInfo(DBG_IO, "IOCTL_ZYHELLO_SET_READ_TIMEOUT");
        if(copyFromRequest(request, &val, sizeof(val)) != sizeof(val)) {
            status = STATUS_INVALID_PARAMETER;
            break;
        }
        deviceContext->readTimeoutMs = val;
        TraceInfo(DBG_IO, "IOCTL_ZYHELLO_SET_READ_TIMEOUT = %d", val);
        break;

    case IOCTL_ZYHELLO_SET_WRITE_TIMEOUT:
        if(copyFromRequest(request, &val, sizeof(val)) != sizeof(val)) {
            status = STATUS_INVALID_PARAMETER;
            break;
        }
        deviceContext->writeTimeoutMs = val;
        TraceInfo(DBG_IO, "IOCTL_ZYHELLO_SET_WRITE_TIMEOUT = %d", val);
        break;

    default:
        TraceError(DBG_IO, "Unknown IOCTL code!");
        status = STATUS_NOT_SUPPORTED;
        break;
    }

    if(!NT_SUCCESS(status)) { WdfRequestComplete(request, status); }
    TraceVerbose(DBG_IO, "%!FUNC! exit with status: %!STATUS!", status);
    return;
}

// Callback function on Device node ReadFile
VOID zyhelloEvtIoRead(IN WDFQUEUE queue, IN WDFREQUEST request, IN size_t length) {
    NTSTATUS        status        = STATUS_SUCCESS;
    PDEVICE_CONTEXT deviceContext = DeviceGetContext(WdfRequestGetFileObject(request));
    UNREFERENCED_PARAMETER(length);  // length can be extracted from request
    TraceVerbose(DBG_IO, "(Queue=%p, Request=%p, Length=%llu)", queue, request, length);
    TraceVerbose(DBG_IO, "readTimeoutMs = %d", deviceContext->readTimeoutMs);

    // copy to request memory
    size_t dataSize = copyToRequest(request, deviceContext->demoBuf, strlen(deviceContext->demoBuf));

    WdfRequestCompleteWithInformation(request, status, dataSize);
    TraceVerbose(DBG_IO, "zyhelloEvtIoRead: %llu bytes", dataSize);
}

// Callback function on Device node write operations
VOID zyhelloEvtIoWrite(IN WDFQUEUE queue, IN WDFREQUEST request, IN size_t length) {
    NTSTATUS        status        = STATUS_SUCCESS;
    PDEVICE_CONTEXT deviceContext = DeviceGetContext(WdfRequestGetFileObject(request));
    UNREFERENCED_PARAMETER(length);  // length can be extracted from request
    TraceVerbose(DBG_IO, "(Queue=%p, Request=%p)", queue, request);
    TraceVerbose(DBG_IO, "writeTimeoutMs = %d", deviceContext->writeTimeoutMs);

    // copy to request memory
    size_t dataSize = copyFromRequest(request, deviceContext->demoBuf, strlen(deviceContext->demoBuf));

    WdfRequestCompleteWithInformation(request, status, dataSize);
    TraceVerbose(DBG_IO, "zyhelloEvtIoWrite: %llu bytes", dataSize);
}

/**
This event is invoked for a power-managed queue before the device leaves the working state (D0).
@param Queue -  Handle to the framework queue object that is associated with the I/O request.
@param Request - Handle to a framework request object.
@param ActionFlags - A bitwise OR of one or more WDF_REQUEST_STOP_ACTION_FLAGS-typed flags that identify the reason that the callback function is being called and whether the request is cancelable.
@return VOID
*/
VOID zyhelloEvtIoStop(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ ULONG ActionFlags) {
    TraceInfo(DBG_INIT, "%!FUNC! Queue 0x%p, Request 0x%p ActionFlags %d", Queue, Request, ActionFlags);

    //
    // In most cases, the EvtIoStop callback function completes, cancels, or postpones
    // further processing of the I/O request.
    //
    // Typically, the driver uses the following rules:
    //
    // - If the driver owns the I/O request, it calls WdfRequestUnmarkCancelable
    //   (if the request is cancelable) and either calls WdfRequestStopAcknowledge
    //   with a Requeue value of TRUE, or it calls WdfRequestComplete with a
    //   completion status value of STATUS_SUCCESS or STATUS_CANCELLED.
    //
    //   Before it can call these methods safely, the driver must make sure that
    //   its implementation of EvtIoStop has exclusive access to the request.
    //
    //   In order to do that, the driver must synchronize access to the request
    //   to prevent other threads from manipulating the request concurrently.
    //   The synchronization method you choose will depend on your driver's design.
    //
    //   For example, if the request is held in a shared context, the EvtIoStop callback
    //   might acquire an internal driver lock, take the request from the shared context,
    //   and then release the lock. At this point, the EvtIoStop callback owns the request
    //   and can safely complete or requeue the request.
    //
    // - If the driver has forwarded the I/O request to an I/O target, it either calls
    //   WdfRequestCancelSentRequest to attempt to cancel the request, or it postpones
    //   further processing of the request and calls WdfRequestStopAcknowledge with
    //   a Requeue value of FALSE.
    //
    // A driver might choose to take no action in EvtIoStop for requests that are
    // guaranteed to complete in a small amount of time.
    //
    // In this case, the framework waits until the specified request is complete
    // before moving the device (or system) to a lower power state or removing the device.
    // Potentially, this inaction can prevent a system from entering its hibernation state
    // or another low system power state. In extreme cases, it can cause the system
    // to crash with bugcheck code 9F.
    //

    return;
}
